﻿// -----------------------------------------------------------------------
// <copyright file="Morphology.cs" company="FCIS">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------

namespace LastVersionOfIpPackage.Operations
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using LastVersionOfIpPackage.Enum_needed;

    /// <summary>
    /// TODO: Update summary.
    /// </summary>
    public class Morphology
    {
        /// <summary>
        /// static object of this class to use this without create object
        /// </summary>
        private static Morphology instance = new Morphology();

        /// <summary>
        /// Gets or sets the instance.
        /// </summary>
        /// <value>
        /// The instance.
        /// </value>
        public static Morphology Instance
        {
            get { return Morphology.instance; }
            set { Morphology.instance = value; }
        }

        /// <summary>
        /// Apply the morphology to image.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="type">The type of morphology.</param>
        /// <param name="setB">The set B.</param>
        /// <param name="thresholdValue">The threshold value.</param>
        /// <param name="original">The original point in set b.</param>
        /// <returns>modified image</returns>
        public Bitmap MakeMorphology(Bitmap image, MorphologyType type, int[,] setB, int thresholdValue, Point original)
        {
            Color[,] bufferOfImage = BufferOfImage.Buffer.GetBuffer(HistoOperation.HistorOperation.Threshold(image, thresholdValue));
            if (type == MorphologyType.Dilation)
            {
                this.ReflectSet(ref setB, ref original);
                return BufferOfImage.Buffer.GetImage(this.MakeMorphologyDialation(setB, bufferOfImage, original));
            }
            else if (type == MorphologyType.Erosion)
            {
                return BufferOfImage.Buffer.GetImage(this.MakeMorphologyErosion(setB, bufferOfImage, original));
            }

            return null;
        }

        /// <summary>
        /// Padings the specified buffer of image.
        /// </summary>
        /// <param name="bufferOfImage">The buffer of image.</param>
        /// <param name="widthOfSetB">The width of set B.</param>
        /// <param name="heigthOfSetB">The heigth of set B.</param>
        /// <param name="original">The original.</param>
        /// <returns>new buffer after padding</returns>
        private Color[,] Pading(Color[,] bufferOfImage, int widthOfSetB, int heigthOfSetB, Point original)
        {
            Color[,] newBufferOfImage = new Color[bufferOfImage.GetLength(0) + widthOfSetB - 1, bufferOfImage.GetLength(1) + heigthOfSetB - 1];
            int numberOfPixelInLeft;
            int numberOfPixelInAbove;
            numberOfPixelInLeft = original.X;
            numberOfPixelInAbove = original.Y;
            for (int i = 0; i < newBufferOfImage.GetLength(1); i++)
            {
                for (int j = 0; j < newBufferOfImage.GetLength(0); j++)
                {
                    if ((j > numberOfPixelInLeft - 1 && j < bufferOfImage.GetLength(0) + numberOfPixelInLeft) && (i > numberOfPixelInAbove - 1 && i < bufferOfImage.GetLength(1) + numberOfPixelInAbove))
                    {
                        newBufferOfImage[j, i] = bufferOfImage[j - numberOfPixelInLeft, i - numberOfPixelInAbove];
                    }
                    else
                    {
                        newBufferOfImage[j, i] = Color.FromArgb(0, 0, 0);
                    }
                }
            }

            return newBufferOfImage;
        }

        /// <summary>
        /// Apply the morphology dialation.
        /// </summary>
        /// <param name="setB">The set B.</param>
        /// <param name="bufferOfImage">The buffer of image.</param>
        /// <param name="original">The original of set b.</param>
        /// <returns>buffer of new image</returns>
        private Color[,] MakeMorphologyDialation(int[,] setB, Color[,] bufferOfImage, Point original)
        {
            int numberOfAddInRigth = setB.GetLength(0) - original.X - 1;
            int numberOfAddInBlow = setB.GetLength(1) - original.Y - 1;
            int numberOfAddInLeft = original.X;
            int numberOfAddInAbove = original.Y;
            Color[,] newBuffer = new Color[bufferOfImage.GetLength(0), bufferOfImage.GetLength(1)];
           bool found = false;
            for (int i = 0; i < bufferOfImage.GetLength(1); i++)
            {
                for (int j = 0; j < bufferOfImage.GetLength(0); j++)
                {
                    found = false;
                    int z = 0;
                    int f = 0;
                    for (int h = i - numberOfAddInAbove; h <= i + numberOfAddInBlow; h++)
                    {
                        z = 0;
                        for (int w = j - numberOfAddInLeft; w <= j + numberOfAddInRigth; w++)
                        {
                            if (w >= 0 && w < bufferOfImage.GetLength(0) && h >= 0 && h < bufferOfImage.GetLength(1))
                            {
                                if (setB[z, f] == bufferOfImage[w, h].R)
                                {
                                    if (setB[z, f] == 255)
                                    {
                                        newBuffer[j, i] = Color.FromArgb(255, 255, 255);
                                        found = true;
                                        break;
                                    }
                                    else
                                    {
                                        newBuffer[j, i] = Color.FromArgb(0, 0, 0);
                                    }
                                }
                            }

                            if (f == setB.GetLength(1) - 1 && z == setB.GetLength(0) - 1)
                            {
                                newBuffer[j, i] = Color.FromArgb(0, 0, 0);
                            }

                            z++;
                        }

                        f++;

                        if (found)
                        {
                            break;
                        }
                    }
                }
            }

            return newBuffer;
        }

        /// <summary>
        /// Reflects the set b in case of dialation.
        /// </summary>
        /// <param name="setB">The set B.</param>
        /// <param name="original">The original point in set b.</param>
        private void ReflectSet(ref int[,] setB, ref Point original)
        {
            bool modified = false;
            for (int j = 0; j < (setB.GetLength(0) + 1) / 2; j++)
            {
                for (int i = 0; i < setB.GetLength(1); i++)
                {
                    int swapPoint;
                    swapPoint = setB[j, i];
                    if (j == original.X && i == original.Y)
                    {
                        if (modified == false)
                        {
                            original.X = setB.GetLength(0) - 1 - j;
                            original.Y = setB.GetLength(1) - 1 - i;
                            modified = true;
                        }
                    }
                    else if (original.X == setB.GetLength(0) - 1 - j && original.Y == setB.GetLength(1) - 1 - i)
                    {
                        if (modified == false)
                        {
                            original.X = j;
                            original.Y = i;
                            modified = true;
                        }
                    }

                    setB[j, i] = setB[setB.GetLength(0) - 1 - j, setB.GetLength(1) - 1 - i];
                    setB[setB.GetLength(0) - 1 - j, setB.GetLength(1) - 1 - i] = swapPoint;
                }
            }
        }

        /// <summary>
        /// Apply the morphology erosion.
        /// </summary>
        /// <param name="setB">The set B.</param>
        /// <param name="bufferOfImage">The buffer of image.</param>
        /// <param name="original">The original point in set b.</param>
        /// <returns>
        /// buffer of modified image
        /// </returns>
        private Color[,] MakeMorphologyErosion(int[,] setB, Color[,] bufferOfImage, Point original)
        {
            int numberOfAddInRigth = setB.GetLength(0) - original.X - 1;
            int numberOfAddInBlow = setB.GetLength(1) - original.Y - 1;
            int numberOfAddInLeft = original.X;
            int numberOfAddInAbove = original.Y;
            Color[,] newBuffer = new Color[bufferOfImage.GetLength(0), bufferOfImage.GetLength(1)];
            bool found = false;
            for (int i = 0; i < bufferOfImage.GetLength(1); i++)
            {
                for (int j = 0; j < bufferOfImage.GetLength(0); j++)
                {
                    found = false;
                    int z = 0;
                    int f = 0;
                    for (int h = i - numberOfAddInAbove; h <= i + numberOfAddInBlow; h++)
                    {
                        z = 0;
                        for (int w = j - numberOfAddInLeft; w <= j + numberOfAddInRigth; w++)
                        {
                            if (w >= 0 && w < bufferOfImage.GetLength(0) && h >= 0 && h < bufferOfImage.GetLength(1))
                            {
                                if (setB[z, f] == 255)
                                {
                                    if (setB[z, f] != bufferOfImage[w, h].R)
                                    {
                                        newBuffer[j, i] = Color.FromArgb(0, 0, 0);
                                        found = true;
                                        break;
                                    }
                                }
                            }
                            else
                            {
                                if (setB[z, f] == 255)
                                {
                                    newBuffer[j, i] = Color.FromArgb(0, 0, 0);
                                    found = true;
                                    break;
                                }
                            }

                            if (f == setB.GetLength(1) - 1 && z == setB.GetLength(0) - 1)
                            {
                                newBuffer[j, i] = Color.FromArgb(255, 255, 255);
                            }

                            z++;
                        }

                        f++;

                        if (found)
                        {
                            break;
                        }
                    }
                }
            }

            return newBuffer;
        }
    }
}
